local pairs = pairs
local ipairs = ipairs
local unpack = unpack
local assert = assert
local remove = table.remove
local insert = table.insert
local open = io.open
local format = string.format
local join = string.join

local G = _G
local log = log

module("bindings")

local broadcast, parse, call, set

actions = {}
bindings = {}
modclicks = {}
-- set = nil

function GetKey(action)
	for _, binding in ipairs(bindings) do
		if action == binding.action then
			return unpack(binding.keys)
		end
	end
end

function GetAction(key)
	for _, binding in ipairs(bindings) do
		for _, k in ipairs(binding.keys) do
			if key == k then
				return binding.action
			end
		end
	end
end

local function clearkey(key)
	for _, binding in ipairs(bindings) do
		for index, k in ipairs(binding.keys) do
			if key == k then
				remove(binding.keys, index)
				return
			end
		end
	end
end

function Set(key, action, no_event)
	clearkey(key)
	local binding
	for _, v in ipairs(bindings) do
		if v.action == action then
			binding = v
			break
		end
	end
	if not binding then
		binding = {
			action = action,
			keys = {},
		}
		insert(bindings, binding)
	end
	insert(binding.keys, key)
	if not no_event then
		if not broadcast then
			broadcast = G.UI.BroadcastEvent
		end
		broadcast("UPDATE_BINDINGS")
	end
end

function GetSet()
	return set
end

function Count()
	return #bindings
end

function Get(i)
	local binding = bindings[i]
	return bindings.action, unpack(bindings.keys)
end

function CountModClickActions()
	return #modclicks
end

function GetModClickAction(i)
	return modclicks[i].action
end

function SetModClickAction(action, binding)
	binding = (binding ~= "NONE") and binding
	for i = 1, #modclicks do
		local modclick = modclicks[i]
		if modclick.action == action then
			modclick.binding = binding
			return
		end
	end
	insert(modclicks, {	action = action, binding = binding })
end

function GetModClick(action)
	for i = 1, #modclicks do
		local modclick = modclicks[i]
		if modclick.action == action then
			return modclick.binding or "NONE"
		end
	end
end

function IsModClick(action)
	return false -- @fixme
end

local files = {
	[0] = "wtf/bindings-cache.wtf",
	[1] = "save/account/bindings-cache.wtf",
	[2] = "save/character/bindings-cache.wtf",
}

function Load(which)
	local file = files[which]
	assert(file)
	if not parse then
		parse = G.wtf.Parse
	end
	parse(file)
	set = which
end

function Save(which)
	local file = files[which]
	assert(which ~= 0 and file)
	local f = open(file, "w")
	for _, binding in ipairs(bindings) do
		for _, key in ipairs(binding.keys) do
			f:write(format("bind %s %s\n", key, binding.action))
		end
	end
	for _, modclick in ipairs(modclicks) do
		f:write(format("modclick %s %s\n", modclick.action, modclick.binding or "NONE"))
	end
	f:close()
end

function Declare(name, onUp, handler)
	local v = actions[name]
	if not v then
		v = {}
		actions[name] = v
	end
	if onUp then
		v.up = handler
	end
	v.down = handler
end

function Run(name, state)
	local v = actions[name]
	if not v then
		log("EE Unknown action %s Run", name)
		return
	end
	local handler = v[state]
	if handler then
		if not call then
			call = G.api.call
		end
		call(function () handler(state) end)
	end
end

function Handle(key, state)
	for _, binding in ipairs(bindings) do
		for _, k in ipairs(binding.keys) do
			if key == k then
				Run(binding.action, state)
			end
		end
	end
end

function Dump()
	local f = open("bindings.txt", "w")
	for name in pairs(actions) do
		local s = name
		for _, binding in ipairs(bindings) do
			if binding.action == name then
				s = s.." : "..join(", ", unpack(binding.keys))
				break
			end
		end
		f:write(format("%s\n", s))
	end
	f:close()
end
